package com.xyy.saas.payment.adpater.jd.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wangyin.aks.pdf.util.PKCS12Keystore;
import com.wangyin.aks.security.api.CryptoClientService;
import com.wangyin.aks.security.api.CryptoClientServiceImpl;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.jd.common.JDReturnTextTypeEnum;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.Base64;
import com.xyy.saas.payment.util.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 请求Util
 */
@Slf4j
@Component
public class JDRequestUtils {

    // 锁
    private static Lock lock = new ReentrantLock();
    // map：存储密钥信息
    private static Map<String, String> CERT_MAP = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        try {

            getCERT("133400097004", "pfx");
            getCERT("133400097004", "cer");
            log.info("初始化秘钥信息完成：133400097004 pfx");
            log.info("初始化秘钥信息完成：133400097004 cer");
            getCERT("135647451008", "pfx");
            getCERT("135647451008", "cer");
            log.info("初始化秘钥信息完成：135647451008 pfx");
            log.info("初始化秘钥信息完成：135647451008 cer");
            getCERT("133400097002", "pfx");
            getCERT("133400097002", "cer");
            log.info("初始化秘钥信息完成：133400097002 pfx");
            log.info("初始化秘钥信息完成：133400097002 cer");
        } catch (IOException e) {
            throw new RuntimeException("初始化秘钥信息异常");
        }
    }

    /**
     * 获取密钥信息
     */
    private static String getCERT(String merchantNo, String type) throws IOException {
        String key = merchantNo + "_" + type;
        if (CERT_MAP.containsKey(key)) {
            return CERT_MAP.get(key);
        }
        InputStream inputStream = null;
        lock.lock();
        try {
            if (CERT_MAP.containsKey(key)) {
                return CERT_MAP.get(key);
            }
            inputStream = new ClassPathResource("jd_" + merchantNo + "." + type).getInputStream();
            String encode = Base64.encode(IOUtils.toByteArray(inputStream));
            CERT_MAP.put(key, encode);
            return encode;
        } finally {
            lock.unlock();
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) { }
            }
        }
    }

    /**
     * 请求demo(带证书加密) 外部商户https参考使用
     */
    public static String tradeRequestSSL(String logPrefix, String merchantNo, Map<String, String> reqMap, String jsonParam, String url) throws Exception {
        String responseText;
        try {
            log.info("{} [jdPay]1.报文头请求参数= {}", logPrefix, reqMap);
            log.info("{} [jdPay]2.报文业务请求参数= {}", logPrefix, jsonParam);
            String signType = reqMap.get(JDConstants.PARAM_SIGN_TYPE); // 签名类型
            String encryptionType = reqMap.get(JDConstants.PARAM_ENC_TYPE); // 加密类型
            byte[] dataBytes = jsonParam.getBytes(JDConstants.CHARSET_UTF8);
            String priCert = getCERT(merchantNo, "pfx");
            String pubCert = getCERT(merchantNo, "cer");
            CryptoClientService se = new CryptoClientServiceImpl();
            PKCS12Keystore keystore = PKCS12Keystore.getInstance(Base64.decode(priCert), JDConstants.merchantPassword(logPrefix, merchantNo));
            String res = se.signEnvelop(keystore, pubCert, dataBytes);
            //log.info("{} [jdPay]3.业务报文加密，密文：AP7 encryptData= {}", logPrefix, res);
            reqMap.put(JDConstants.PARAM_ENC_DATA, res);
            reqMap.put(JDConstants.PARAM_ENC_TYPE, encryptionType);
            //log.info("{} [jdPay]4.加签前报文内容，reqMap= {}", logPrefix, reqMap);
            String sign = JDSignUtils.sign(reqMap, signType, JDConstants.merchantSignKey(logPrefix, merchantNo), JDConstants.CHARSET_UTF8, "NPP2");
            log.info("{} [jdPay]5.加签签名，signData= {}", logPrefix, sign);
            reqMap.put(JDConstants.PARAM_SIGN, sign);
            reqMap.put(JDConstants.PARAM_SIGN_TYPE, signType);

            log.info("{} [jdPay]6.httpURL: {}", logPrefix, url);
//            log.info("{} [jdPay]7.reqParams: {}", logPrefix, JSONObject.toJSONString(reqMap));
            JDWyHttpClientUtil util = new JDWyHttpClientUtil();
            if (url.indexOf("https") > -1) {
                responseText = util.postSSLUrlWithParams(logPrefix, url, reqMap);
            } else {
                responseText = util.postUrlWithParams(logPrefix, url, reqMap);
            }
//            log.info("{} [jdPay]8.http resp msg: {}", logPrefix, responseText);
        } catch (Exception e) {
            log.error("{} [jdPay]invocation of exceptions, url={}, param={}, errorMsg={}, error={}", logPrefix, url, jsonParam, e.getMessage(), e);
            if (JDConstants.ALARM_URL.contains(url)) {
                DingTalkMessage msg = DingTalkMessage.builder()
                        .type("调用京东接口异常")
                        .method("JDRequestUtils.tradeRequestSSL#" + url)
                        .msg("接口：" + url + "调用失败")
                        .param(jsonParam)
                        .build();
                SpringUtils.getBean(AlarmService.class).alarm(msg);
            }
            throw e;
        }
        return responseText;
    }

    /**
     * 验签并返回参数
     * 适用于返回参数没有敏感信息未加密场景
     */
    public static JSONObject verifySignAndReturn(String logPrefix, String merchantNo, String responseText) throws Exception {
        JSONObject jsonObject = verifySignAndReturnBase(logPrefix, merchantNo, responseText, JDReturnTextTypeEnum.NATURAL);
        if (null == jsonObject) {
            // 接口调用失败，请检查签名或加密是否正确，若检查无误可联系技术人员处理
            log.error("签名验证不过或响应结果为空，响应结果为=", responseText);
            throw new Exception("调用接口失败！");
        }
        return jsonObject;
    }

    /**
     * 验签并返回参数
     * 适用于返回参数有敏感信息加密场景
     */
    public static JSONObject verifySignAndDecryptReturn(String logPrefix, String merchantNo, String responseText) throws Exception {
        return verifySignAndReturnBase(logPrefix, merchantNo, responseText, JDReturnTextTypeEnum.ENCRYPT);
    }

    /**
     * 验签并返回参数
     */
    private static JSONObject verifySignAndReturnBase(String logPrefix, String merchantNo, String responseText, JDReturnTextTypeEnum returnTextType) throws Exception {
        if (StringUtils.isEmpty(responseText)) {
            return null;
        }
        if (!responseText.startsWith("{")) {
            log.info("{} response error result：{}", logPrefix, responseText);
            return null;
        }
        JSONObject jsonObject = null;
        try {
            Map<String, String> contentMap = (Map<String, String>) JSON.parse(responseText);
            String signType = contentMap.get(JDConstants.PARAM_SIGN_TYPE);
            String resSignData = contentMap.remove(JDConstants.PARAM_SIGN);
            String signData = JDSignUtils.sign(contentMap, signType, JDConstants.merchantSignKey(logPrefix, merchantNo), JDConstants.CHARSET_UTF8, "NPP2");
            //log.info("{} res signature= {}", logPrefix, resSignData);
            log.info("{} signature= {}", logPrefix, signData);
            if (!resSignData.equals(signData)) {
                log.error("{} the signature is error", logPrefix);
            } else {
                log.info("{} the signature is correct", logPrefix);
            }
            String data = contentMap.get("respData");
            if (null == data) {
                return jsonObject;
            }
            String clearData = dealReturnText(merchantNo, data, returnTextType);
//            log.info("{} response clearData = {}", logPrefix, clearData);
            if (StringUtils.isNotEmpty(clearData)) {
                jsonObject = JSONObject.parseObject(clearData);
            }
        } catch (Exception e) {
            log.error("{} return parameter transformation exception {}", logPrefix, e);
            throw e;
        }
        return jsonObject;
    }

    private static String dealReturnText(String merchantNo, String text, JDReturnTextTypeEnum returnTextType) throws Exception {
        if (JDReturnTextTypeEnum.ENCRYPT.equals(returnTextType)) {
            return JDAES.decryptBase64(text, JDConstants.merchantAesKey("dealReturnText", merchantNo), "UTF-8");
        } else {
            return JDCodecUtils.decodeBase64(text, false);
        }
    }

}
